package com.yc.system.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yc.framework.utils.IntUtil;
import com.yc.framework.utils.ObjectUtils;
import com.yc.framework.utils.QRCodeUtil;
import com.yc.framework.utils.StringUtil;
import com.yc.sssmpg.dto.ProductDto;
import com.yc.system.dto.Filterbook;
import com.yc.system.dto.Issure;
import com.yc.system.dto.esmodel.BookInfo;
import com.yc.system.entity.Book;
import com.yc.system.entity.BookType;
import com.yc.system.mapper.BookMapper;
import com.yc.system.mapper.BookTypeMapper;
import com.yc.system.service.IBookService;
import com.yc.framework.baseClass.BaseServiceImpl;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import com.yc.framework.baseClass.R;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * <p>
 * 图书信息表 服务实现类
 * </p>
 *
 * @author 1850140986@qq.com
 * @since 2022-12-03
 */

@Service
//查询成功（会多生成一个无名文件夹）
//@CacheConfig(cacheNames = "cache:user")

//@Cacheable(key = "#root.methodName", unless = "#result==null")
//@CacheEvict(key = "#username")
public class BookServiceImpl extends BaseServiceImpl<BookMapper, Book> implements IBookService {

    @Value("${file.upload.path}")
    private String path;
    @Value("${file.upload.relativePath}")
    private String relativePath;

    @Autowired
    public BookMapper bookMapper;

    @Autowired
    public BookTypeMapper bookTypeMapper;

    //高亮功能被整合在了此对象中
    @Resource
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    /**
     * 分页查询实体列表
     *
     * @param model 查询参数
     * @return 查询结果
     */
    @Override
    public R list(Book model) {
        IPage<Book> page = new Page<>(model.getPageNum(), model.getPageSize());
        QueryWrapper<Book> wrapper = new QueryWrapper<>();
        initCondition(wrapper);
        setParam(wrapper, model);
        IPage<Book> px = page(page, wrapper);
        return R.ok().put("data", px.getRecords()).put("total", px.getTotal());
    }

    /**
     * 根据主键删除实体对象，返回值封装成R，若成功则会把要删除的主键计入返回的R
     *
     * @param id 主键
     * @return 删除是否成功
     * @Override public R removeModelById(String id){
     * Book model=new Book();
     * model.setId(id);
     * return removeModelById(model);
     * }
     */
    @Override
    public void setParam(QueryWrapper<Book> wrapper, Book model) {
        if (model == null) {
            return;
        }
        // wrapper.eq("deleted", false);
//        折损depreciation
//        typeId,userId,publisher,author,n_price,，keyword，isbn，sellNum，like_num
        wrapper.like(model.getBookName() != null, "book_name", model.getBookName())
                .like(model.getPublisher() != null && model.getPublisher().length() > 0, "publisher", model.getPublisher())
                .like(model.getAuthor() != null, "author", model.getAuthor())
                .like(model.getKeyword() != null, "keyword", model.getKeyword())
                .eq(model.getIsbn() != null, "isbn", model.getIsbn())
                .eq(model.getTypeId() != null, "type_id", model.getTypeId())
        ;
    }

    //条件筛选
    @Override
    public R Filterbook(Filterbook filterbook) {

        Book model=(Book)filterbook;

//        IPage<Book> page = new Page<>(model.getPageNum(), model.getPageSize());
//        QueryWrapper<Book> wrapper = new QueryWrapper<>();
//        initCondition(wrapper);
//        setParam(wrapper, model);
//
//        wrapper.inSql(filterbook.getIssuerName().length()>0,"user_id","select user_id from t_store where store_name like '%"+filterbook.getIssuerName()+"%'");
//        wrapper.ge(filterbook.getLprice()>0,"n_price",filterbook.getLprice());
//        wrapper.le(filterbook.getHprice()>0,"n_price",filterbook.getHprice());
//        System.out.println("filterbook========="+filterbook);
//        IPage<Book> px = page(page, wrapper);
//        return R.ok().put("data", px.getRecords()).put("total", px.getTotal());

        return R.ok().put("data",bookMapper.filterbook(model));
//        return R.ok().put("data",bookMapper.filterbook(model,page, wrapper, 1));
    }

    @Override
    public R pay() {
//        创建：  /files/pay/ef103b9f-e634-48d6-ae19-27868573979c.jpg

//        String originalFileName = "E:\\usedbook\\javafiles\\pay\\x.jpg";//原始文件名
//        String name = String.format("%s%s", UUID.randomUUID().toString().toLowerCase(),
//                originalFileName.substring(originalFileName.lastIndexOf(".")));
//        String pathDir = "/pay/";
//        String pathName = pathDir + name;  //父文件夹加名字
//        String reqFullFileName = this.relativePath + pathName;   //相对地址
//        String realPath = this.path + pathDir;   //硬盘绝对文件夹
//        File dir = new File(realPath);
//        if (!dir.exists()) {
//            dir.mkdirs();
//        }
//        String path=realPath+name;
//        QRCodeUtil.encode("http://192.168.2.103:8080/toPay", path);
//        return R.ok().put("payAddr", reqFullFileName).put("path",path);

        return R.ok().put("payAddr", "/files/pay/ef103b9f-e634-48d6-ae19-27868573979c.jpg");

    }



    //  返回bookId以及coverPhoto
    @Override
    public R uploadCoverPhoto(int bookId, String reqFullFileName) {
//        判断是否新建
        if (IntUtil.isEmpty(bookId)) {
            Book book = new Book();
            book.setCoverPhoto(reqFullFileName);
            book.setBookId(bookId);
            System.out.println(reqFullFileName);
            saveModel(book);
            int db_bookId = getBookIdByCoverPhoto(reqFullFileName);
            System.out.println("BookServiceImpl=>db_bookId:" + db_bookId);
            return R.ok().put("bookId", db_bookId).put("coverPhotoAddr", reqFullFileName);
        } else {

            String db_coverPhotoAddr = getCoverPhotoByBookId(bookId);
            String db_1coverPhotoAddr = db_coverPhotoAddr.replace(relativePath + "/", "");
            String coverPhotoAddr = db_1coverPhotoAddr.replaceAll("/", "\\\\");
            String realPath = null;
            try {
                realPath = path + URLDecoder.decode(coverPhotoAddr, "UTF-8");
            } catch (IOException e) {
                e.printStackTrace();
                return R.err().put("message", "文件地址错误" + e.getMessage());
            }
            File f = new File(realPath);
            System.out.println("是否已经存在，需要删除原保存的图片-----------------------------------：" + realPath + f.exists());
            if (f.delete()) {
                System.out.println("File deleted successfully------------------------");
            } else {
                System.out.println("Failed to delete the file----------------------");
            }
            int i =updateCoverPhotoByBookId(bookId, reqFullFileName);
            return R.ok().put("bookId", bookId).put("coverPhotoAddr", reqFullFileName);
        }
    }

    @Override
    public R updateBybookId(Book model) {
        return bookMapper.updateBybookId(model)==1?R.ok("更新成功"):R.err("更新失败");
    }

    public String getCoverPhotoByBookId(int bookId) {
        return bookMapper.getCoverPhotoByBookId(bookId);
    }

    public int updateCoverPhotoByBookId(int bookId, String reqFullFileName) {
        return bookMapper.updateCoverPhotoByBookId(bookId, reqFullFileName);
    }

    public int getBookIdByCoverPhoto(String reqFullFileName) {
        return bookMapper.getBookIdByCoverPhoto(reqFullFileName);
    }


    @Override
    public R selectBooksByUserId(int userId){
        List<Book> bookList=bookMapper.selectBooksByUserId(userId);
        return R.ok().put("bookList",bookList);
    }

    @Override
    public R getSwiper() {
        List<Book> swiperList=bookMapper.getSwiper();
        return R.ok().put("swiperList",swiperList);
    }

    public R getBookByBookId(int bookId){
        return R.ok().put("bookInfo",bookMapper.getBookByBookId(bookId));
    }

    public R getBooksByPageByUserId(IPage<Book> page, QueryWrapper<Book> wrapper, int i,int userId) {
        return R.ok().put("bookInfo",bookMapper.getBooksByPageByUserId(page,wrapper,i,userId)).put("total",bookMapper.getTotal(userId));
    }


    @Override
    public R getBooksByPageRandom(IPage<Book> page, QueryWrapper<Book> wrapper, int i) {
        return R.ok().put("bookInfo",bookMapper.getBooksByPageRandom(page,wrapper,i));
    }

    @Override
    public R getBooksByTypeId(Book findByPage) {

        int pId=bookTypeMapper.getTypePIdbyTypeId(findByPage.getTypeId());
        System.out.println("---------------pId:"+pId);
        if(pId==-1){
            return R.ok().put("bookInfo",getBooksByPId(findByPage,pId));
        }else{
            IPage<Book> page = new Page<>(findByPage.getPageNum() ,findByPage.getPageSize());
            QueryWrapper<Book> wrapper = new QueryWrapper<>();
//            wrapper.eq("type_id",findByPage.getTypeId());
            return R.ok().put("bookInfo",getBooksByPageAndTypeId(page, wrapper, 1,findByPage.getTypeId()));
        }
    }



    public List<Book> getBooksByPageAndTypeId(IPage<Book> page, QueryWrapper<Book> wrapper, int i, Integer typeId) {
        return bookMapper.getBooksByPageAndTypeId(page,wrapper,1,typeId);
    }

    public List<Book>  getBooksByPId(Book findByPage,Integer pId) {
        IPage<Book> page = new Page<>(findByPage.getPageNum() ,findByPage.getPageSize());
        QueryWrapper<Book> wrapper = new QueryWrapper<>();
        return bookMapper.getBooksByPId(page,wrapper,1,pId);
    }

    //高亮
    @Override
    public R filterBooksByES(Filterbook page) {
        System.out.println("page.getPageNum()"+page.getPageNum());
        //1 -------------准备一个ES提供的索引查询构造器  这个类可以和SortBuilders配合指定更加丰富的排序
        NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();
        //2 -------------封装分页对象
        Pageable pageable = PageRequest.of(Integer.parseInt(String.valueOf(page.getPageNum()))-1,Integer.parseInt(String.valueOf(page.getPageSize())));
        searchQueryBuilder.withPageable(pageable);

        //3 -------------封装查询条件 这个类和QueryBuilders配合可以搭配出丰富的查询条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //筛选属性： keyword,author,publisher,issuerName,bookId,hprice,lprice
        if (StringUtil.isNotEmpty(page.getBookName())){
            boolQueryBuilder.must(QueryBuilders.matchQuery("bookName",page.getBookName()));
        }
        if (StringUtil.isNotEmpty(page.getKeyword())){
            boolQueryBuilder.must(QueryBuilders.fuzzyQuery("keyword",page.getKeyword()).fuzziness(Fuzziness.ONE));
        }
        if (StringUtil.isNotEmpty(page.getAuthor())){
            boolQueryBuilder.must(QueryBuilders.fuzzyQuery("author",page.getAuthor()).fuzziness(Fuzziness.ONE));
        }
        if (StringUtil.isNotEmpty(page.getPublisher())){
            boolQueryBuilder.must(QueryBuilders.fuzzyQuery("publisher",page.getPublisher()).fuzziness(Fuzziness.ONE));
        }
        if (StringUtil.isNotEmpty(page.getIssuerName())){
            boolQueryBuilder.must(QueryBuilders.fuzzyQuery("issuerName",page.getIssuerName()).fuzziness(Fuzziness.ONE));
        }
        System.out.println("ES");
        System.out.println("page:"+page);
        System.out.println("ObjectUtils.isNotEmpty(page.getTypeId())):"+ObjectUtils.isNotEmpty(page.getTypeId()));
        if (ObjectUtils.isNotEmpty(page.getTypeId())){
            if(page.getTypeId()>20){ //不为父Id
                boolQueryBuilder.must(QueryBuilders.termQuery("typeId",page.getTypeId()));
            }else{
                boolQueryBuilder.must(QueryBuilders.termQuery("pId",page.getTypeId()));
            }
        }
        //价格：hprice,lprice
        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
        if (ObjectUtils.isNotEmpty(page.gethPrice())){
                rangeQuery.lte(page.gethPrice());
        }
        if (ObjectUtils.isNotEmpty(page.getlPrice())){
            rangeQuery.gte(page.getlPrice());
        }

            //精确查询 (完全等于)- 不分词
            // 多个值 title = ‘ss’ or name = ‘mm’    QueryBuilders.termsQuery("title","mm",”ss“)
            //termQuery
            //模糊查询 - 分词
            //boolQueryBuilder.must(QueryBuilders.matchQuery("category",kw));
            // QueryBuilders.queryStringQuery(kw).defaultField("title");
            //不指定feild，查询范围为所有feild
            //QueryBuilders.queryStringQuery("青春");
            //指定多个feild
            //QueryBuilders.queryStringQuery("青春").field("title").field("category");
            //多项模糊匹配
            //boolQueryBuilder.should(QueryBuilders.multiMatchQuery(kw, "title", "category"));
            //左右模糊查询
            //QueryBuilders.fuzzyQuery("title",kw).fuzziness(Fuzziness.ONE);
           //前缀查询
           //QueryBuilders.prefixQuery("title", "开发开放")
           //通配符查询，支持*和？，？表示单个字符；注意不建议将通配符作为前缀，否则导致查询很慢
           //QueryBuilders.wildcardQuery("title", "开*放")
           //QueryBuilders.wildcardQuery("title", "开？放")


        //3.2 根据指定字段区间查询   from(gte) 大于等于    to(lte)  小于等于
//        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
//        if(kw!= null){
//            rangeQuery.from(kw);
//        }
//        if(kw != null){
//            rangeQuery.to(kw);
//        }
//        boolQueryBuilder.must(rangeQuery);


        //配置好后应当提交给索引查询构造器，但本案例中是为了让大家知道有这个东西就行
        //至于用这两个字段过滤的功能在高亮运行时就已经实现了，同时也体现出一个事情：
        //   高亮和ES的查询构造器不是必须共生，而是高亮是索引查询的一个功能
        //searchQueryBuilder.withFilter(boolQueryBuilder);

        //4 --排序-----------
        // 自定义一个ES的排序 指定SUM是匹配度总和 以及最小匹配度值
        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(boolQueryBuilder).scoreMode(FunctionScoreQuery.ScoreMode.SUM).setMinScore(1);
        searchQueryBuilder.withQuery(functionScoreQueryBuilder);
        //属性排序
//      searchQueryBuilder.withSort(Sort.by("id"));

        //5 -------------封装配置的高亮字段 (个数：Highlightcount): bookName,keyword,author,publisher,issuerName

        HighlightBuilder.Field[] fields = new HighlightBuilder.Field[5];
        fields[0] = new HighlightBuilder.Field("bookName").preTags("<font color='red'>").postTags("</font>").requireFieldMatch(false);
        fields[1] = new HighlightBuilder.Field("keyword").preTags("<font color='red'>").postTags("</font>").requireFieldMatch(false);
        fields[2] = new HighlightBuilder.Field("author").preTags("<font color='red'>").postTags("</font>").requireFieldMatch(false);
        fields[3] = new HighlightBuilder.Field("publisher").preTags("<font color='red'>").postTags("</font>").requireFieldMatch(false);
        fields[4] = new HighlightBuilder.Field("issuerName").preTags("<font color='red'>").postTags("</font>").requireFieldMatch(false);
        searchQueryBuilder.withHighlightFields(fields);

        //6 -------------用索引条件构造器生成一个查询条件对象
        NativeSearchQuery searchQuery = searchQueryBuilder.build();

        //7 -------------查询
        SearchHits<BookInfo> searchHits = elasticsearchRestTemplate.search(searchQuery, BookInfo.class);

        if(searchHits.getTotalHits()<=0){
            return null;
        }

        //8 处理结果
        List<BookInfo> list = new ArrayList<>();
        Map<String,Object> searchResultMap = new HashMap<>();

        List<SearchHit<BookInfo>> result = searchHits.getSearchHits();
        result.forEach(r -> {
            //原始数据
            BookInfo product = r.getContent();
            //高亮数据  bookName,keyword,author,publisher,issuerName
            Map<String, List<String>> highlightFields = r.getHighlightFields();
            //处理 bookName 字段
            List<String> title = highlightFields.get("bookName");
            if (title != null && title.size() > 0) {
                StringBuffer buffer = new StringBuffer();
                for (String s : title) {
                    buffer.append(s);
                }
                product.setBookName(buffer.toString());
            }
            //处理 keyword 字段
            List<String> category = highlightFields.get("keyword");
            if (category != null && category.size() > 0) {
                StringBuffer buffer = new StringBuffer();
                for (String s : category) {
                    buffer.append(s);
                }
                product.setKeyword(buffer.toString());
            }
            //处理 author 字段
            List<String> author = highlightFields.get("author");
            if (author != null && author.size() > 0) {
                StringBuffer buffer = new StringBuffer();
                for (String s : author) {
                    buffer.append(s);
                }
                product.setAuthor(buffer.toString());
            }//处理 publisher 字段
            List<String> publisher = highlightFields.get("publisher");
            if (publisher != null && publisher.size() > 0) {
                StringBuffer buffer = new StringBuffer();
                for (String s : publisher) {
                    buffer.append(s);
                }
                product.setPublisher(buffer.toString());
            }//处理 issuerName 字段
            List<String> issuerName = highlightFields.get("issuerName");
            if (issuerName != null && issuerName.size() > 0) {
                StringBuffer buffer = new StringBuffer();
                for (String s : issuerName) {
                    buffer.append(s);
                }
                product.setIssuerName(buffer.toString());
            }

            //处理age字段---------------------高亮(int)类型
//            List<String> ages = highlightFields.get("age");
//            if (ages != null && ages.size() > 0) {
//                StringBuffer buffer = new StringBuffer();
//                //这里这个循环，大家不要有什么疑惑，就是springboot框架返回了一个集合，其实里面就一个高亮后的数据
//                for (String s : ages) {
//                    buffer.append(s);
//                }
//                //实体bean需要新增一个字段，因为原来的是数字，高亮后是个字符串
//                user.setAge_HT(buffer.toString());
//            }

            //装入数据集
            list.add(product);
        });
        searchResultMap.put("dataList",list);
        //保存分页信息：总条数total、总页数pages、结果当前页currPage
        long hitsTotalValue = searchHits.getTotalHits();
        searchResultMap.put("total", hitsTotalValue);
        searchResultMap.put("pages", (long) Math.ceil(hitsTotalValue / page.getPageSize()));
        searchResultMap.put("currPage", page.getPageNum());
//        elasticsearchRestTemplate.matchAllQuery();
        System.out.println("ES:------------------------------------");
        System.out.println(list);
        System.out.println("total:"+ hitsTotalValue);
        System.out.println("currPage:"+ page.getPageNum());
        return R.ok().put("data",searchResultMap);
    }


    @Override
    public R getlikeByPageByUserId(IPage<Book> page, QueryWrapper<Book> wrapper, int i, Integer userId) {
        return R.ok().put("bookInfo",bookMapper.getlikeByPageByUserId(page,wrapper,i,userId)).put("total",bookMapper.getlikeTotal(userId));
    }
}
